package comp302.prototype.component;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.util.Iterator;

import comp302.prototype.misc.MiscMath;
import comp302.prototype.settings.LineSettings;
import comp302.prototype.xml.XMLNode;

public class Line implements Component {
	private Point start, end, lowPoint, highPoint;
	private int thickness;
	private Color color;
	private int selected = 0;
	private final int NOTHING = 0, P1 = 1, P2 = 2, LINE = 3;
	
	public Line() {
		start = new Point();
		end = new Point();
		color = Color.RED;
		thickness = 3;
		refreshHighLow();
	}

	public Line(int x1, int y1, int x2, int y2, Color color, int thickness) {
		start = new Point(x1, y1);
		end = new Point(x2, y2);
		this.color = color;
		this.thickness = thickness;
		refreshHighLow();
	}

	public Line(Point start, Point end, Color color, int thickness) {
		this.start = start;
		this.end = end;
		this.color = color;
		this.thickness = thickness;
		refreshHighLow();
	}

	@Override
	public String getName() {
		return "Line";
	}

	public void setStartPoint(int x, int y) {
		start = new Point(x, y);
		refreshHighLow();
	}

	public void setEndPoint(int x, int y) {
		end = new Point(x, y);
		refreshHighLow();
	}

	private void refreshHighLow() {
		lowPoint = new Point((int) Math.min(start.getX(), end.getX()),
				(int) Math.min(start.getY(), end.getY()));
		highPoint = new Point((int) Math.max(start.getX(), end.getX()),
				(int) Math.max(start.getY(), end.getY()));
	}

	@Override
	public void draw(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		Color previousColor = g.getColor();
		Stroke previousStroke = g2d.getStroke();

		g.setColor(color);
		g2d.setStroke(new BasicStroke(thickness));
		g2d.drawLine((int) start.getX(), (int) start.getY(), (int) end.getX(),
				(int) end.getY());
		drawSelection(g);
		g2d.setStroke(previousStroke);
		g.setColor(previousColor);
	}

	private void drawSelection(Graphics g) {
		int tx, ty, x1, y1, x2, y2;
		Graphics2D g2d = (Graphics2D) g;

		switch (selected) {
		case P1:
			tx = (int) start.getX();
			ty = (int) start.getY();
			x1 = tx - 4;
			y1 = ty - 4;
			x2 = tx + 4;
			y2 = ty + 4;
			break;

		case P2:
			tx = (int) end.getX();
			ty = (int) end.getY();
			x1 = tx - 4;
			y1 = ty - 4;
			x2 = tx + 4;
			y2 = ty + 4;
			break;

		case LINE:
			x1 = (int) lowPoint.getX();
			y1 = (int) lowPoint.getY();
			x2 = (int) highPoint.getX();
			y2 = (int) highPoint.getY();
			break;

		default:
			return;
		}

		x1 -= thickness;
		y1 -= thickness;
		x2 += thickness;
		y2 += thickness;

		g2d.setStroke(new BasicStroke(2));
		g.setColor(Color.BLACK);
		g2d.drawLine(x1, y1, x1 + thickness, y1);
		g2d.drawLine(x1, y1, x1, y1 + thickness);
		g2d.drawLine(x2, y2, x2 - thickness, y2);
		g2d.drawLine(x2, y2, x2, y2 - thickness);
	}

	@Override
	public boolean isPointInside(Point p) {

		if (lowPoint.getX() <= p.getX() && p.getX() <= highPoint.getX()
				&& lowPoint.getY() <= p.getY() && p.getY() <= highPoint.getY()) {
			return true;
		} else if (p.distance(start) <= thickness * 3
				|| p.distance(end) <= thickness * 3) {
			return true;
		}

		return false;
	}

	@Override
	public boolean select(int x, int y) {
		Point selectionPoint = new Point(x, y);

		if (selectionPoint.distance(start) <= thickness + 3) {
			selected = P1;
			return true;
		} else if (selectionPoint.distance(end) <= thickness + 3) {
			selected = P2;
			return true;
		} else if (MiscMath.distanceToLine(start, end, selectionPoint) <= thickness + 3) {
			selected = LINE;
			return true;
		} else {
			selected = NOTHING;
			return false;
		}
	}

	@Override
	public void deselect() {
		selected = NOTHING;
	}

	@Override
	public boolean isSelected() {
		return (selected != NOTHING) ? true : false;
	}

	@Override
	public void move(int x, int y) {

		switch (selected) {
		case P1:
			start.translate(x, y);
			break;

		case P2:
			end.translate(x, y);
			break;

		case LINE:
			start.translate(x, y);
			end.translate(x, y);
			lowPoint.translate(x, y);
			highPoint.translate(x, y);
			return;

		default:
			return;
		}

		refreshHighLow();
	}

	@Override
	public void updateAttributes() {
		thickness = LineSettings.getThickness();
		color = LineSettings.getColor();
	}

	@Override
	public boolean boundaryCheck(int width, int height) {
		double xl = lowPoint.getX();
		double yl = lowPoint.getY();
		double xh = highPoint.getX();
		double yh = highPoint.getY();

		return (xl < 0 || yl < 0 || xh > width || yh > height) ? false : true;
	}
	
	@Override
	public XMLNode toXML() {
		XMLNode node = new XMLNode("line");
		
		node.setAttribute("thickness", Integer.toString(thickness));
		node.setAttribute("color", Integer.toString(color.getRGB()));
		
		XMLNode startNode = new XMLNode("point");
		startNode.setAttribute("x", Integer.toString((int) start.getX()));
		startNode.setAttribute("y", Integer.toString((int) start.getY()));
		node.addChild(startNode);
		
		XMLNode endNode = new XMLNode("point");
		endNode.setAttribute("x", Integer.toString((int) end.getX()));
		endNode.setAttribute("y", Integer.toString((int) end.getY()));
		node.addChild(endNode);
		
		return node;
	}
	
	@Override
	public String toString() {
		return toXML().toString();
	}

	@Override
	public void fromXML(XMLNode node) {
		int x, y;
		Iterator<XMLNode> iterator = node.childIterator();
		thickness = Integer.parseInt(node.getAttribute("thickness"));
		color = new Color(Integer.parseInt(node.getAttribute("color")));
		
		XMLNode startNode = iterator.next();
		x = Integer.parseInt(startNode.getAttribute("x"));
		y = Integer.parseInt(startNode.getAttribute("y"));
		start = new Point(x, y);
		
		XMLNode endNode = iterator.next();
		x = Integer.parseInt(endNode.getAttribute("x"));
		y = Integer.parseInt(endNode.getAttribute("y"));
		end = new Point(x, y);
		
		refreshHighLow();
	}
}
